//
//  MZUIScrollView+Combine.swift
//  MZTool
//
//  Created by gby on 2022/3/11.
//

import UIKit
import WebKit

public enum CombineScrollDirection{
    case horizontal
    case vertical
}

fileprivate extension UIView {
    
    private struct UIViewCombineKey {
        static var cacheLastContentSize_key = "mz_cacheLastContentSize_key"
        static var cacheLastFrame_key = "mz_cacheLastFrame_key"
        static var cacheRelativeFrame_key = "mz_cacheRelativeFrame_key"
    }
    
    var cacheContentSize: String? {
        get { return objc_getAssociatedObject(self, &UIViewCombineKey.cacheLastContentSize_key) as? String}
        set { objc_setAssociatedObject(self, &UIViewCombineKey.cacheLastContentSize_key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
    }
    
    var cacheSelfFrame: String? {
        get { return objc_getAssociatedObject(self, &UIViewCombineKey.cacheLastFrame_key) as? String}
        set { objc_setAssociatedObject(self, &UIViewCombineKey.cacheLastFrame_key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
    }
    
    var cacheRelativeFrame: String? {
        get { return objc_getAssociatedObject(self, &UIViewCombineKey.cacheRelativeFrame_key) as? String}
        set { objc_setAssociatedObject(self, &UIViewCombineKey.cacheRelativeFrame_key, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) }
    }
}


public class MZCombineScrollView: UIScrollView, UIScrollViewDelegate {
    
    var scrollDirectoin: CombineScrollDirection = .horizontal
    var innerViewList:[UIView] = []
    
    private var currentContentOffset:CGPoint = CGPoint.zero
    private var stopViews: [UIView] = []
    
    //监听列表
    var observations: [NSKeyValueObservation] = []
    
    deinit {
        if #available(iOS 11.0, *) {} else {
            for oneobserve in observations {
                oneobserve.invalidate()
            }
            observations.removeAll()
        }
    }
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        delegate = self
    }
    
    public convenience init(frame: CGRect, childViews: [UIView], directoin: CombineScrollDirection) {
        self.init(frame:frame)
        scrollDirectoin = directoin
        innerViewList = childViews
        innerViewsInit()
        superview?.layoutIfNeeded()
        refreshChildLayout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //MARK: 子视图配置
    func innerViewsInit() {
        while self.subviews.count > 0 {
            self.subviews.last?.removeFromSuperview()
        }
        for (_, view) in innerViewList.enumerated() {
            if let scrol = view as? UIScrollView {
                scrol.showsVerticalScrollIndicator = false
                scrol.showsHorizontalScrollIndicator = false
                scrol.isScrollEnabled = false
                childScrolObserveInit(scrol: scrol)
            }
            if let webview = view as? WKWebView {
                webview.scrollView.showsVerticalScrollIndicator = false
                webview.scrollView.showsHorizontalScrollIndicator = false
                webview.scrollView.isScrollEnabled = false
                childScrolObserveInit(scrol: webview.scrollView)
            }
            self.addSubview(view)
            self.sendSubviewToBack(view)
        }
    }
    
    //MARK: 子视图监听
    func childScrolObserveInit(scrol: UIScrollView) {
        observations.append(
            scrol.observe(\.contentSize, options: [.new, .old]) { [weak self] s, change in
                let oldsize = NSCoder.cgSize(for: s.cacheContentSize ?? "")
                if !oldsize.equalTo(change.newValue ?? CGSize.zero) {
                    s.cacheContentSize = NSCoder.string(for: change.newValue ?? CGSize.zero)
                    self?.refreshChildLayout()
                }
            }
        )
    }
    
    //MARK: 子视图添加完成后重新布局
    func refreshChildLayout() {
        var mainWith = self.mz.width
        var mainHeight = self.mz.height
        
        var innerContentWidthSum:CGFloat = 0
        var innerContentHeightSum:CGFloat = 0
        
        var currentX:CGFloat = 0
        var currentY:CGFloat = 0
        
        for (_, view) in innerViewList.enumerated() {
            var viewSize = NSCoder.cgSize(for: view.cacheContentSize ?? "")
            if CGSize.zero.equalTo(viewSize) {
                if let scrol = view as? UIScrollView {
                    viewSize = scrol.contentSize
                }else if let webview = view as? WKWebView {
                    viewSize = webview.scrollView.contentSize
                }else {
                    viewSize = view.mz.size
                }
            }
            if scrollDirectoin == .vertical {
                let scFith = viewSize.height > mainHeight ? mainHeight : viewSize.height
                view.frame = CGRect(x: currentX, y: currentY, width: mainWith, height: scFith)
                currentY = view.frame.maxY
                view.cacheSelfFrame = NSCoder.string(for: view.frame)
                
                var orgFrame = view.frame
                orgFrame.origin.y = innerContentHeightSum
                orgFrame.size.height = viewSize.height
                view.cacheRelativeFrame = NSCoder.string(for: orgFrame)
                
                innerContentHeightSum += viewSize.height
            }else {
                let scFitw = viewSize.height > mainWith ? mainWith : viewSize.width
                view.frame = CGRect(x: currentX, y: currentY, width: scFitw, height: mainHeight)
                currentX = view.frame.maxX
                view.cacheSelfFrame = NSCoder.string(for: view.frame)
                
                var orgFrame = view.frame
                orgFrame.origin.y = innerContentWidthSum
                orgFrame.size.width = viewSize.width
                view.cacheRelativeFrame = NSCoder.string(for: orgFrame)
                
                innerContentWidthSum += viewSize.width
            }
            view.setNeedsLayout()
            view.layoutIfNeeded()
        }
        for (_, _) in innerViewList.enumerated() {
            //FIXME 头部是需要暂停的
        }
        if scrollDirectoin == .vertical {
            mainHeight = max(mainHeight, innerContentHeightSum)
        }else{
            mainWith = max(mainWith, innerContentWidthSum)
        }
        self.contentSize = CGSize(width: mainWith, height: mainHeight)
        self.scrollViewDidScroll(self)
    }
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        currentContentOffset = self.contentOffset
        if let targetView = self.findCurrentScrolToView() {
            self.updateCurrentScrolToView(target: targetView)
        }
    }
    
    func findCurrentScrolToView() -> UIView?{
        var offsetXY:CGFloat = 0
        var targetView: UIView?
        if scrollDirectoin == .vertical {
            offsetXY = currentContentOffset.y
        }else{
            offsetXY = currentContentOffset.x
        }
        var offsetSum:CGFloat = 0
        for (_, view) in innerViewList.enumerated() {
            var fitSize = CGSize.zero
            if let scrol = view as? UIScrollView {
                fitSize = scrol.contentSize
            }else if let webview = view as? WKWebView {
                fitSize = webview.scrollView.contentSize
            }else {
                fitSize = view.mz.size
            }
            if scrollDirectoin == .vertical {
                offsetSum += fitSize.height
                if offsetXY < offsetSum {
                    targetView = view
                    break
                }
            }else{
                offsetSum += fitSize.width
                if offsetXY < offsetSum {
                    targetView = view
                    break
                }
            }
        }
        return targetView
    }
    
    func updateCurrentScrolToView(target: UIView) {
        
        //FIXME: 先处理悬浮的视图
        if let scrol = target as? UIScrollView {
            var frameX:CGFloat = 0
            var frameY:CGFloat = 0
            var contentX:CGFloat = 0
            var contentY:CGFloat = 0
            
            let frame = NSCoder.cgRect(for: scrol.cacheSelfFrame ?? "")
            
            if scrollDirectoin == .vertical {
                let relativeFrame = NSCoder.cgRect(for: scrol.cacheRelativeFrame ?? "")
                contentY = currentContentOffset.y - relativeFrame.origin.y
                //已经滑倒底
                let maxContentY: CGFloat = scrol.contentSize.height - frame.size.height
                if contentY >= maxContentY {
                    frameY = relativeFrame.maxY - frame.size.height
                    contentY = maxContentY
                }else{
                    frameY = currentContentOffset.y
                }
            }else{
                let relativeFrame = NSCoder.cgRect(for: scrol.cacheRelativeFrame ?? "")
                contentY = currentContentOffset.x - relativeFrame.origin.x
                let maxContentX: CGFloat = scrol.contentSize.width - frame.size.width
                if contentX >= maxContentX {
                    frameX = relativeFrame.maxX - frame.size.width
                    contentX = maxContentX
                }else{
                    frameX = currentContentOffset.x
                }
            }
            scrol.frame = CGRect(x: frameX, y: frameY, width: scrol.mz.width, height: scrol.mz.height)
            scrol.contentOffset = CGPoint(x: contentX, y: contentY)
        }else if let webview = target as? WKWebView {
            var frameX:CGFloat = 0
            var frameY:CGFloat = 0
            var contentX:CGFloat = 0
            var contentY:CGFloat = 0
            let scrol = webview.scrollView
            
            let frame = NSCoder.cgRect(for: webview.cacheSelfFrame ?? "")
            
            if scrollDirectoin == .vertical {
                let relativeFrame = NSCoder.cgRect(for: webview.cacheRelativeFrame ?? "")
                contentY = currentContentOffset.y - relativeFrame.origin.y
                //已经滑倒底
                let maxContentY: CGFloat = scrol.contentSize.height - frame.size.height
                if contentY >= maxContentY {
                    frameY = relativeFrame.maxY - frame.size.height
                    contentY = maxContentY
                }else{
                    frameY = currentContentOffset.y
                }
            }else{
                let relativeFrame = NSCoder.cgRect(for: webview.cacheRelativeFrame ?? "")
                contentY = currentContentOffset.x - relativeFrame.origin.x
                let maxContentX: CGFloat = scrol.contentSize.width - frame.size.width
                if contentX >= maxContentX {
                    frameX = relativeFrame.maxX - frame.size.width
                    contentX = maxContentX
                }else{
                    frameX = currentContentOffset.x
                }
            }
            webview.frame = CGRect(x: frameX, y: frameY, width: webview.mz.width, height: webview.mz.height)
            scrol.contentOffset = CGPoint(x: contentX, y: contentY)
        }
        
        let curIndex = innerViewList.firstIndex(of: target)
        
        var frontView = target
        if let index = curIndex {
            for behandIndex in (index + 1)..<innerViewList.count {
                let behandView = innerViewList[behandIndex]
                var behandCacheRect =  NSCoder.cgRect(for: behandView.cacheSelfFrame ?? "")
                if true {//FIXME：悬停视图
                    if scrollDirectoin == .vertical {
                        behandCacheRect.origin.y = NSCoder.cgRect(for: behandView.cacheRelativeFrame ?? "").minY
                    }else{
                        behandCacheRect.origin.x = NSCoder.cgRect(for: behandView.cacheRelativeFrame ?? "").minX
                    }
                    behandView.frame = behandCacheRect
                }
                if let behandScrol = behandView as? UIScrollView {
                    behandScrol.contentOffset = CGPoint.zero
                }else if let webview = behandView as? WKWebView {
                    webview.scrollView.contentOffset = CGPoint.zero
                }
                frontView = behandView
            }
        }
    }
}
